/**
 * Copyright (c) 2018-2023, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 *
 * Contains: Implementation of virtio-entropy
 *
 * Change Logs:
 * Date           Author            Notes
 * 2023-07-21     Shiroko           Init
 */

#include "nxos.h"

#ifdef CONFIG_NX_DRIVER_VIRTIO_ENTROPY

#undef NX_LOG_MOD_NAME
#define NX_LOG_MOD_NAME "VIRTIO-ENTROPY"

#include "base/log.h"

#define DRV_NAME "virtio entropy"
#define DEV_NAME "virtio_entropy"

#include "../virtio.h"

/* Driver Operations */
NX_PRIVATE NX_Error open(struct NX_Device *device, NX_U32 flags)
{
    // Negotiate features & status
    VirtioDeviceInitialize(device, NX_NULL, 0);
    // Device-specified initialization
    VirtioQueueAlloc(device, 1);
    VirtioQueueInit(device, 0, CONFIG_NX_DRIVER_VIRTIO_SPLIT_QUEUE_SIZE);
    // Driver OK
    VirtioDeviceDriverOK(device);
    VirtioGetExtension(device)->flags = flags;

    return NX_EOK;
}

NX_PRIVATE NX_Error close(struct NX_Device *device)
{
    // Reset the device
    VirtioDeviceReset(device);
    // Cleanup VirtioQueue
    for (int i = 0; i < VirtioGetExtension(device)->queue_num; i++)
        VirtioQueueDestroy(device, i);
    VirtioQueueFree(device);
    return NX_EOK;
}


NX_PRIVATE NX_Error read(struct NX_Device *device, void *buf, NX_Offset off, NX_Size len, NX_Size *outLen)
{
    NX_ASSERT(buf != NX_NULL);
    NX_ASSERT(len != 0);
    char *buffer = NX_MemAlloc(len);
    if (!buffer) return NX_ENOMEM;

    VirtioQueue *queue = VirtioQueueGet(device, 0);
    NX_U32      idx    = VirtioQueueDescAlloc(queue);
    VirtioQueueDescFill(queue, idx,
                        (NX_U64) ((NX_UArch) NX_Virt2Phy(buffer)),
                        len, VIRTIO_QUEUE_DESC_FLAG_WRITE, 0);
    VirtioQueuePutIntoAvailRing(queue, idx);
    VirtioSendNotification(device, 0, idx);
    VirtioWaitNotification(device, 0, idx);
    VirtioQueueDescFree(queue, idx);

    NX_Process *process = NX_ProcessCurrent();
    if (process)
    {
        char *pbuf = (void *) NX_VmspaceVirToPhy(&process->vmspace, (NX_Addr) buf);
        if (pbuf == NX_NULL)
        {
            buf = NX_Virt2Phy(buf);
        }
        else
        {
            buf = pbuf;
        }
    }
    else
    {
        buf = NX_Virt2Phy(buf);
    }

    NX_MemCopy(buf, buffer, len);
    *outLen = len;

    NX_MemFree(buffer);

    return NX_EOK;
}

NX_PRIVATE NX_Error write(struct NX_Device *device, void *buf, NX_Offset off, NX_Size len, NX_Size *outLen)
{
    return NX_EOK;
}


NX_PRIVATE NX_DriverOps virtioEntropyDriverOps = {
        .open = open,
        .close = close,
        .read = read,
        .write = write,
};

NX_PRIVATE NX_Error VirtioEntropyInit(NX_Driver *driver, NX_Device *device)
{
    return NX_EOK;
}

NX_PRIVATE VirtioDriverTableEntity virtioEntropyDriverTableEntity = {
        .deviceId = VIRTIO_DEVICE_ID_ENTROPY,
        .driverName = "virtio-entropy",
        .deviceNameTemplate = "virtio_entropy_%d",
        .deviceType = NX_DEVICE_TYPE_CHAR,
        .driverOps = &virtioEntropyDriverOps,
        .initHandler = VirtioEntropyInit,
        .cleanUpHandler = NX_NULL
};

VirtioDriverTableEntity *VirtioDriverTableEntityForVirtioEntropy(void)
{
    return &virtioEntropyDriverTableEntity;
}

#else
#include "../virtio.h"

VirtioDriverTableEntity *VirtioDriverTableEntityForVirtioEntropy(void)
{
    return NX_NULL;
}

#endif // CONFIG_NX_DRIVER_VIRTIO_ENTROPY
